package org.jetlinks.pro.notify.wechat.corp;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.jetlinks.core.Values;
import org.jetlinks.pro.notify.AbstractNotifier;
import org.jetlinks.pro.notify.DefaultNotifyType;
import org.jetlinks.pro.notify.NotifyType;
import org.jetlinks.pro.notify.Provider;
import org.jetlinks.pro.notify.template.TemplateManager;
import org.jetlinks.pro.notify.wechat.WechatProvider;
import org.jetlinks.pro.notify.wechat.WeixinGeneralRequest;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import javax.annotation.Nonnull;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicReference;

@Slf4j
public class WeixinCorpNotifier extends AbstractNotifier<WechatMessageTemplate> {

    private final AtomicReference<String> accessToken = new AtomicReference<>();

    private long refreshTokenTime;

    private final long tokenTimeOut = Duration.ofSeconds(7000).toMillis();

    private final WebClient client;

    private static final String tokenApi = "https://qyapi.weixin.qq.com/cgi-bin/gettoken";

    private static final String notify = "https://qyapi.weixin.qq.com/cgi-bin/message/send";

    private final WechatCorpProperties properties;

    @Getter
    private final String notifierId;

    public WeixinCorpNotifier(String id, WebClient client, WechatCorpProperties properties, TemplateManager templateManager) {
        super(templateManager);
        this.client = client;
        this.properties = properties;
        this.notifierId = id;
    }

    @Nonnull
    @Override
    public NotifyType getType() {
        return DefaultNotifyType.weixin;
    }

    @Nonnull
    @Override
    public Provider getProvider() {
        return WechatProvider.corpMessage;
    }

    @Nonnull
    @Override
    public Mono<Void> send(@Nonnull WechatMessageTemplate template, @Nonnull Values context) {
        return getToken()
            .flatMap(token -> WeixinGeneralRequest
                .sendPost(client,
                          UriComponentsBuilder
                          .fromUriString(notify)
                          .queryParam("access_token", token)
                          .toUriString(),
                          template.createJsonRequest(context)
                )
            )
            .then();
    }

    private Mono<String> getToken() {
        if (System.currentTimeMillis() - refreshTokenTime > tokenTimeOut || accessToken.get() == null) {
            return requestToken();
        }
        return Mono.just(accessToken.get());
    }

    private Mono<String> requestToken() {
        return WeixinGeneralRequest
            .requestToken(client,
                          UriComponentsBuilder
                              .fromUriString(tokenApi)
                              .queryParam("corpid", properties.getCorpId())
                              .queryParam("corpsecret", properties.getCorpSecret())
                              .build()
                              .toUri()
            )
            .doOnNext((r) -> {
                refreshTokenTime = System.currentTimeMillis();
                accessToken.set(r);
            });
    }

    @Nonnull
    @Override
    public Mono<Void> close() {
        accessToken.set(null);
        refreshTokenTime = 0;
        return Mono.empty();
    }
}
